# root = tk.Tk()
# root.overrideredirect(True)
# root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
# # #开启主循环，让窗口处于显示状态
# root.mainloop()
import datetime

from funcall.FuncallInfo import FuncallInfo, aiapp, db
import os
import sys

datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 配置
# { "name": "嘟嘟传奇金币版", "conf": [{"name": '',"clip":"要复制的内容","warp": "字符串[true:换行,false: 不换行]"}]}

from tkinter import *
import tkinter.filedialog  # 注意次数要将文件对话框导入
import json
from tkinter import ttk, messagebox  # 导入ttk模块以使用Treeview

width = 850
heigth = 500

root = Tk()
buttons = []
fiList = {}
# 添加下拉选择框选择每页显示行数
per_page_var = IntVar(value=20)
per_page_options = [10, 20, 50, 100]

# 初始化当前页码
current_page = 1

# 初始化排序变量
sort_column = None
sort_order = "asc"

#用于列表中显示标题属性对应的中文
fieldMap={
    'id': '主键',
    'description': '描述',
    'req_args': '请求参数',
    'create_time': '创建时间',
    'remark': '备注',
    'status': '状态',
    'completion_tokens': '完成令牌',
    'prompt_tokens': '提示令牌',
    'total_tokens': '总令牌'
}

class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        #print(f'{"*"*30}DateEncoder{"*"*30}')
        if isinstance(obj, datetime.datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")
        else:
            return json.JSONEncoder.default(self, obj)


def load_config():
    global root
    global width
    global heigth

    # 使用 pyinstaller 打包后,路径不对,使用该方式获取可用的配置文件目录 start
    if getattr(sys, 'frozen', False):
        absPath = os.path.dirname(os.path.abspath(sys.executable))
    elif __file__:
        absPath = os.path.dirname(os.path.abspath(__file__))
    # 使用 pyinstaller 打包后,路径不对,使用该方式获取可用的配置文件目录 end

    print("\nabsPath=" + absPath)
    # 读取json文件
    with open(absPath + '/AiLogView.json', 'r', encoding='utf-8') as f:
        tmp = json.load(f)
        print(tmp)
        width = tmp['width']
        heigth = tmp['heigth']
        win_width = root.winfo_screenwidth() - width
        win_height = root.winfo_screenheight() - heigth
        print('width=%s,heigth=%s' % (win_width, win_height))

        root.geometry('%sx%s+%s+%s' % (width, heigth, 300, 300))
        # root.destroy()
        # root.mainloop()


def sort_treeview(column):
    global current_page
    global sort_order
    global sort_column

    # 切换排序顺序
    if column == sort_column:
        sort_order = "desc" if sort_order == "asc" else "asc"
    else:
        sort_order = "asc"
        sort_column = column

    # 更新列标题的箭头图标
    for col in tree["columns"]:
        if col == column:
            tree.heading(col, text=f"{fieldMap[col]} {arrow_down if sort_order == 'asc' else arrow_up}")
        else:
            tree.heading(col, text=fieldMap[col])

    search_query = search_entry.get()
    current_page = 1
    update_listbox(current_page, per_page_var.get(), search_query, column, sort_order)


def update_listbox(page=1, per_page=20, search_query=None, sort_column=None, sort_order="asc", log_type=None):
    global fiList
    fiList = query_funcall_info_table(page, per_page, search_query, sort_column, sort_order, log_type)
    tree.delete(*tree.get_children())  # 清空Treeview
    if fiList.items:
        for fi in fiList.items:
            status_tmp = fi.status
            if fi.status == '0':
                status_tmp = '正常'
            elif fi.status == '1':
                status_tmp = '错误'
            elif fi.status == '2':
                status_tmp = '执行'
            tree.insert("", END, values=(fi.id, fi.description, fi.req_args, fi.remark, fi.create_time, status_tmp, fi.completion_tokens, fi.prompt_tokens, fi.total_tokens))
    else:
        tree.insert("", END, values=("N/A", "没有更多日志了"))

    # 更新总记录数显示
    total_count_label.config(text=f"总记录数: {fiList.total}")

    # 根据当前页码和总页数决定是否禁用下一页按钮
    if page >= fiList.pages:
        next_button.config(state=DISABLED)
    else:
        next_button.config(state=NORMAL)

    if page <= 1:
        prev_button.config(state=DISABLED)
    else:
        prev_button.config(state=NORMAL)


def query_funcall_info_table(page=1, per_page=20, search_query=None, sort_column=None, sort_order="asc", log_type=None):
    from funcall.FuncallInfo import FuncallInfo, aiapp
    # 创建数据库表
    with aiapp.app_context():
        # 带搜索和排序的分页查询
        global fiList
        query = FuncallInfo.query
        if search_query:
            query = query.filter(FuncallInfo.req_args.ilike(f'%{search_query}%'))
        if log_type is not None and log_type != '':
            query = query.filter(FuncallInfo.type == log_type)
        if sort_column:
            if sort_order == "asc":
                query = query.order_by(getattr(FuncallInfo, sort_column).asc())
            else:
                query = query.order_by(getattr(FuncallInfo, sort_column).desc())
        fiList = query.paginate(page=page, per_page=per_page)
        return fiList


def on_listbox_double_click(event):
    if tree.selection() is None or len(tree.selection())==0:
        return
    item = tree.selection()[0]
    selected_item = tree.item(item, "values")
    fi_id = int(selected_item[0])
    with aiapp.app_context():
        # 修改: 使用Session.get()替代Query.get()
        fi = db.session.get(FuncallInfo, fi_id)
        if fi:
            detail_window = Toplevel(root)
            detail_window.title("日志详情")

            # 修改窗口大小
            detail_window.geometry("1200x800")

            # 创建标签和文本框来显示每个属性
            labels = ["主键", "描述", "请求参数", "日志信息", "备注", "类型", "状态", "创建时间", "完成令牌", "提示令牌", "总令牌"]
            fields = [fi.id, fi.description, fi.req_args, fi.log_text, fi.remark, fi.type, fi.status, fi.create_time, fi.completion_tokens, fi.prompt_tokens, fi.total_tokens]

            for i, (label_text, field_value) in enumerate(zip(labels, fields)):
                label = Label(detail_window, text=label_text, font=("Arial", 12, "bold"))
                label.grid(row=i, column=0, padx=10, pady=5, sticky=W)

                # print(f"label_text={label_text},field_value={field_value}")
                if label_text in ["请求参数", "日志信息"]:
                    text_widget = Text(detail_window, wrap=WORD, height=10, width=100)  # 调整高度和宽度
                    # 修改: 修复text_widget.insert()方法的调用
                    text_widget.insert(END, '' if field_value is None else field_value)
                    # text_widget.config(state=DISABLED)  # 禁用输入
                    text_widget.grid(row=i, column=1, padx=10, pady=5, sticky=W+E)
                else:
                    entry = Entry(detail_window, width=100)  # 调整宽度
                    entry.insert(END, '' if field_value is None else field_value)
                    # entry.config(state=DISABLED)  # 禁用输入
                    entry.grid(row=i, column=1, padx=10, pady=5, sticky=W+E)

            detail_window.geometry("1200x800")


def next_page():
    global current_page
    global fiList
    if current_page < fiList.pages:  # 确保页码不会超出总页数
        current_page += 1
        search_query = search_entry.get()  # 获取当前的搜索关键字
        update_listbox(page=current_page, per_page=per_page_var.get(), search_query=search_query,sort_column=sort_column,sort_order=sort_order)  # 传递搜索关键字


def prev_page():
    global current_page
    if current_page > 1:
        current_page -= 1
        search_query = search_entry.get()  # 获取当前的搜索关键字
        # update_listbox(current_page, per_page_var.get(), search_query)  # 传递搜索关键字
        update_listbox(page=current_page, per_page=per_page_var.get(), search_query=search_query,sort_column=sort_column,sort_order=sort_order)  # 传递搜索关键字


def clear_database():
    from funcall.FuncallInfo import FuncallInfo, aiapp
    from tkinter import messagebox

    # 弹出确认对话框
    confirm = messagebox.askyesno("确认删除", "您确定要清空所有日志数据吗？")
    if confirm:
        with aiapp.app_context():
            FuncallInfo.query.delete()
            db.session.commit()
            global current_page
            current_page=1
            update_listbox(current_page)


def delete_selected_items():
    selected_items = tree.selection()
    if not selected_items:
        messagebox.showwarning("警告", "请选择要删除的日志项。")
        return

    confirm = messagebox.askyesno("确认删除", "您确定要删除选中的日志项吗？")
    if confirm:
        with aiapp.app_context():
            for item in selected_items:
                item_id = tree.item(item, "values")[0]
                # 修改: 使用Session.get()替代Query.get()
                fi = db.session.get(FuncallInfo, item_id)
                if fi:
                    db.session.delete(fi)
            db.session.commit()
            global current_page
            current_page=1
            update_listbox(current_page)


def search_logs(reset_page_num=True):
    global current_page
    global per_page_var
    search_query = search_entry.get()
    log_type = type_var.get()  # 获取类型选择
    if reset_page_num:
        current_page = 1
    update_listbox(current_page, per_page_var.get(), search_query, sort_column, sort_order, log_type)



def toggle_auto_refresh():
    global auto_refresh_flag
    auto_refresh_flag.set(not auto_refresh_flag.get())
    if auto_refresh_flag.get():
        auto_refresh_button.config(text="关闭自动刷新")
        auto_refresh()
    else:
        auto_refresh_button.config(text="开启自动刷新")
        pass

def auto_refresh():
    if auto_refresh_flag.get():
        search_logs(reset_page_num=False)
        root.after(5000, auto_refresh)

s = ttk.Style()
# 创建下拉菜单
menu = tkinter.Menu(root)

# 添加菜单选项
menu.add_command(label="重新读取配置", command=load_config)
# menu.add_command(label="查看日志", command=lambda: update_listbox(current_page))
# 将下拉菜单关联到主窗口
root.config(menu=menu)

# 添加列表框显示日志
# listbox = Listbox(root)
# listbox.pack(fill=BOTH, expand=True)
# listbox.bind("<Double-Button-1>", on_listbox_double_click)

# 添加搜索框和按钮
search_frame = Frame(root)
search_frame.pack(fill=X)

search_entry = Entry(search_frame, width=50)
search_entry.pack(side=LEFT, padx=5, pady=5)

# 新增类型选择框
type_var = StringVar(value="")
type_options = ["", "tool", "api"]
type_combobox = ttk.Combobox(search_frame, width=10, textvariable=type_var, values=type_options)
type_combobox.pack(side=LEFT, padx=5, pady=5)

search_button = Button(search_frame, text="搜索", command=lambda: search_logs(reset_page_num=True))
search_button.pack(side=LEFT, padx=5, pady=5)

# 新增刷新按钮
refresh_button = Button(search_frame, text="刷新", command=lambda: search_logs(reset_page_num=False))
refresh_button.pack(side=LEFT, padx=5, pady=5)

# 新增自动刷新按钮
auto_refresh_flag = BooleanVar(value=False)
auto_refresh_button = Button(search_frame, text="开启自动刷新", command=lambda: toggle_auto_refresh())
auto_refresh_button.pack(side=LEFT, padx=5, pady=5)

# 添加箭头图标资源
arrow_up = "\u25B2"  # 向上箭头
arrow_down = "\u25BC"  # 向下箭头

tree_panel = Frame(root)
tree_panel.pack(fill=BOTH,expand=True)

# 替换为Treeview
tree = ttk.Treeview(tree_panel, columns=("id", "description", "req_args","remark", "create_time","status","completion_tokens","prompt_tokens","total_tokens"), show="headings")
tree.heading("id", text="主键",  command=lambda: sort_treeview("id"))
tree.heading("description", text="描述", command=lambda: sort_treeview("description"))
tree.heading("req_args", text="请求参数", command=lambda: sort_treeview("req_args"))
tree.heading("remark", text="备注", command=lambda: sort_treeview("remark"))
tree.heading("create_time", text="创建时间", command=lambda: sort_treeview("create_time"))
tree.heading("status", text="状态", command=lambda: sort_treeview("status"))
tree.heading("completion_tokens", text="完成令牌", command=lambda: sort_treeview("completion_tokens"))
tree.heading("prompt_tokens", text="提示令牌", command=lambda: sort_treeview("prompt_tokens"))
tree.heading("total_tokens", text="总令牌", command=lambda: sort_treeview("total_tokens"))

tree.column("id", width=30, anchor="center");
tree.column("description", width=200, anchor="center");
tree.column("req_args", width=200, anchor="w");
tree.column("remark", width=200, anchor="w");
tree.column("create_time", width=100, anchor="center");
tree.column("status", width=10, anchor="center");
tree.column("completion_tokens", width=15, anchor="center");
tree.column("prompt_tokens", width=15, anchor="center");
tree.column("total_tokens", width=15, anchor="center");

# 增加滚动条
tree_scrollbar = ttk.Scrollbar(tree_panel, orient="vertical", command=tree.yview)
tree.configure(yscrollcommand=tree_scrollbar.set)
tree_scrollbar.pack(side=RIGHT, fill=Y)

# 增加水平滚动条
tree_xscrollbar = ttk.Scrollbar(tree_panel, orient="horizontal", command=tree.xview)
tree.configure(xscrollcommand=tree_xscrollbar.set)
tree_xscrollbar.pack(side=BOTTOM, fill=X)

tree.pack(fill=BOTH, expand=True)
tree.bind("<Double-1>", on_listbox_double_click)

# 添加上一页/下一页按钮
button_frame = Frame(root)
button_frame.pack(fill=X)

# 添加删除按钮
delete_button = Button(button_frame, text="删除", command=delete_selected_items)
delete_button.pack(side=LEFT)

# 添加清空按钮
clear_button = Button(button_frame, text="清空", command=clear_database)
clear_button.pack(side=LEFT)


# 添加下一页按钮
next_button = Button(button_frame, text="下一页",state=DISABLED,command=next_page)
next_button.pack(side=RIGHT)
# 添加上一页按钮
prev_button = Button(button_frame, text="上一页",state=DISABLED, command=prev_page)
prev_button.pack(side=RIGHT)

# s.configure('1.TCombobox',width=10,padding=[0,0,0,0])
# per_page_combobox = ttk.Combobox(button_frame,style='1.TCombobox', width=10,height=4, textvariable=per_page_var, values=per_page_options)  # 修改: 将宽度调整为50
per_page_combobox = ttk.Combobox(button_frame,style='', width=10,height=4, textvariable=per_page_var, values=per_page_options)  # 修改: 将宽度调整为50
per_page_combobox.pack(side=RIGHT)#, expand=True 取消后排版正常
per_page_combobox.bind("<<ComboboxSelected>>", lambda event: update_listbox(current_page, per_page_var.get()))

# 添加总记录数显示
total_count_label = Label(button_frame,width=20, text="总记录数: 0")
total_count_label.pack(side=RIGHT)


root.config()
root.title("AI日志查看")

win_width = root.winfo_screenwidth() - width
win_height = root.winfo_screenheight() - heigth
print('width=%s,heigth=%s' % (win_width, win_height))

root.geometry('%sx%s+%s+%s' % (width, heigth, 300, 300))
# root.iconbitmap('C:/Users/Administrator/Desktop/C语言中文网logo.ico')

load_config()

# lb = Label(root,text='',bg='#87CEEB')
# lb.grid(row=0,column=1,padx=5)
# 显示窗口
root.mainloop()
